/*
 * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

package org.box2dflash.collision.shapes {
	import org.box2dflash.common.math.XForm;	
	import org.box2dflash.common.math.Vec2;	
	import org.box2dflash.common.math.Mat22;
	import org.box2dflash.collision.shapes.CircleDef;
	import org.box2dflash.collision.AABB;
	import org.box2dflash.common.*;
	import org.box2dflash.collision.shapes.*;
	import org.box2dflash.collision.*;
	use namespace Internal;

	public class CircleShape extends Shape {
		/// @see Shape::TestPoint
		public override function testPoint(transform:XForm, p:Vec2):Boolean {
			//Vec2 center = transform.position + Mul(transform.R, m_localPosition);
			var tMat:Mat22 = transform.R;
			var dX:Number = transform.position.x + (tMat.col1.x * m_localPosition.x + tMat.col2.x * m_localPosition.y);
			var dY:Number = transform.position.y + (tMat.col1.y * m_localPosition.x + tMat.col2.y * m_localPosition.y);
			//Vec2 d = p - center;
			dX = p.x - dX;
			dY = p.y - dY;
			//return Dot(d, d) <= m_radius * m_radius;
			return (dX * dX + dY * dY) <= m_radius * m_radius;
		}

		/// @see Shape::TestSegment
		public override function testSegment(	transform:XForm,
						lambda:Array, // float pointer
						normal:Vec2, // pointer
						segment:Segment,
						maxLambda:Number):Boolean {
			//Vec2 position = transform.position + Mul(transform.R, m_localPosition);
			var tMat:Mat22 = transform.R;
			var positionX:Number = transform.position.x + (tMat.col1.x * m_localPosition.x + tMat.col2.x * m_localPosition.y);
			var positionY:Number = transform.position.x + (tMat.col1.y * m_localPosition.x + tMat.col2.y * m_localPosition.y);
		
			//Vec2 s = segment.p1 - position;
			var sX:Number = segment.p1.x - positionX;
			var sY:Number = segment.p1.y - positionY;
			//float32 b = Dot(s, s) - m_radius * m_radius;
			var b:Number = (sX * sX + sY * sY) - m_radius * m_radius;
		
			// Does the segment start inside the circle?
			if (b < 0.0) {
				return false;
			}
		
			// Solve quadratic equation.
			//Vec2 r = segment.p2 - segment.p1;
			var rX:Number = segment.p2.x - segment.p1.x;
			var rY:Number = segment.p2.y - segment.p1.y;
			//float32 c =  Dot(s, r);
			var c:Number = (sX * rX + sY * rY);
			//float32 rr = Dot(r, r);
			var rr:Number = (rX * rX + rY * rY);
			var sigma:Number = c * c - rr * b;
		
			// Check for negative discriminant and short segment.
			if (sigma < 0.0 || rr < Number.MIN_VALUE) {
				return false;
			}
		
			// Find the point of intersection of the line with the circle.
			var a:Number = -(c + Math.sqrt(sigma));
		
			// Is the intersection point on the segment?
			if (0.0 <= a && a <= maxLambda * rr) {
				a /= rr;
				//*lambda = a;
				lambda[0] = a;
				//*normal = s + a * r;
				normal.x = sX + a * rX;
				normal.y = sY + a * rY;
				normal.normalize;
				return true;
			}
		
			return false;
		}

		/// @see Shape::ComputeAABB
		public override function computeAABB(aabb:AABB, transform:XForm):void {
			//Vec2 p = transform.position + Mul(transform.R, m_localPosition);
			var tMat:Mat22 = transform.R;
			var pX:Number = transform.position.x + (tMat.col1.x * m_localPosition.x + tMat.col2.x * m_localPosition.y);
			var pY:Number = transform.position.y + (tMat.col1.y * m_localPosition.x + tMat.col2.y * m_localPosition.y);
			aabb.lowerBound = new Vec2(pX - m_radius, pY - m_radius);
			aabb.upperBound = new Vec2(pX + m_radius, pY + m_radius);
		}

		/// @see Shape::ComputeSweptAABB
		public override function computeSweptAABB(	aabb:AABB,
							transform1:XForm,
							transform2:XForm):void {
			var tMat:Mat22;
			//Vec2 p1 = transform1.position + Mul(transform1.R, m_localPosition);
			tMat = transform1.R;
			var p1X:Number = transform1.position.x + (tMat.col1.x * m_localPosition.x + tMat.col2.x * m_localPosition.y);
			var p1Y:Number = transform1.position.y + (tMat.col1.y * m_localPosition.x + tMat.col2.y * m_localPosition.y);
			//Vec2 p2 = transform2.position + Mul(transform2.R, m_localPosition);
			tMat = transform2.R;
			var p2X:Number = transform2.position.x + (tMat.col1.x * m_localPosition.x + tMat.col2.x * m_localPosition.y);
			var p2Y:Number = transform2.position.y + (tMat.col1.y * m_localPosition.x + tMat.col2.y * m_localPosition.y);
		
			//Vec2 lower = Min(p1, p2);
			//Vec2 upper = Max(p1, p2);
		
			//aabb->lowerBound.Set(lower.x - m_radius, lower.y - m_radius);
			aabb.lowerBound = new Vec2((p1X < p2X ? p1X : p2X) - m_radius, (p1Y < p2Y ? p1Y : p2Y) - m_radius);
			//aabb->upperBound.Set(upper.x + m_radius, upper.y + m_radius);
			aabb.upperBound = new Vec2((p1X > p2X ? p1X : p2X) + m_radius, (p1Y > p2Y ? p1Y : p2Y) + m_radius);
		}

		/// @see Shape::ComputeMass
		public override function computeMass(massData:MassData):void {
			massData.mass = m_density * Settings._pi * m_radius * m_radius;
			massData.center.v(m_localPosition);
		
			// inertia about the local origin
			//massData.I = massData.mass * (0.5 * m_radius * m_radius + Dot(m_localPosition, m_localPosition));
			massData.I = massData.mass * (0.5 * m_radius * m_radius + (m_localPosition.x * m_localPosition.x + m_localPosition.y * m_localPosition.y));
		}

		/// Get the local position of this circle in its parent body.
		public function get localPosition():Vec2 {
			return m_localPosition;
		}

		/// Get the radius of this circle.
		public function get radius():Number {
			return m_radius;
		}

		//--------------- Internals Below -------------------
		public function CircleShape(def:ShapeDef) {
			super(def);
		
			//Settings.Assert(def.type == e_circleShape);
			var circleDef:CircleDef = def as CircleDef;
		
			m_type = e_circleShape;
			m_localPosition.v(circleDef.localPosition);
			m_radius = circleDef.radius;
		}

		Internal override function updateSweepRadius(center:Vec2):void {
			// Update the sweep radius (maximum radius) as measured from
			// a local center point.
			//Vec2 d = m_localPosition - center;
			var dX:Number = m_localPosition.x - center.x;
			var dY:Number = m_localPosition.y - center.y;
			dX = Math.sqrt(dX * dX + dY * dY); 
			// length
			//m_sweepRadius = d.length + m_radius - _toiSlop;
			m_sweepRadius = dX + m_radius - Settings._toiSlop;
		}

		// Local position in parent body
		Internal var m_localPosition:Vec2 = new Vec2();
		Internal var m_radius:Number;
	}
}
